package org.ttjhome.html.struts.common;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * ScopeLocal subclass which keeps its data in the user's {@link HttpSession}.
 *
 *
 */
public class SessionLocal<T> extends ScopeLocal<T>
{
	private static final String LOCK_KEY = "org.ttjhome.html.struts.common.SessionLocal.lock";

	/**
	 * true if we should force session creation if none exists yet 
	 */
	private boolean mRequireSession;

	/**
	 * Creates a new SessionLocal instance.
	 */
	public SessionLocal()
	{
		this(null, true);
	}

	/**
	 * Creates a new SessionLocal instance with the specified string as its key.
	 *
	 * @param key key to use for the attribute, or null if you want the
	 * ScopeLocal to invent a unique key for you (recommended)
	 * @param requireSession true if this ScopeLocal should create a session
	 * if one does not yet exist.
	 */
	public SessionLocal(String key, boolean requireSession)
	{
		super(key);
		mRequireSession = requireSession;
	}

	public T get(HttpServletRequest request)
	{
		HttpSession session = request.getSession(mRequireSession);
		if (session == null)
		{
			return initialValue(request);
		}

		return super.get(request);
	}

	public void set(HttpServletRequest request, T value)
	{
		HttpSession session = request.getSession(mRequireSession);
		if (session != null)
		{
			super.set(request, value);
		}
	}

	public void remove(HttpServletRequest request)
	{
		HttpSession session = request.getSession(mRequireSession);
		if (session != null)
		{
			super.remove(request);
		}
	}

	protected T doGet(HttpServletRequest request)
	{
		return (T)request.getSession().getAttribute(mKey);
	}

	protected void doSet(HttpServletRequest request, T value)
	{
		request.getSession().setAttribute(mKey, value);
	}

	protected void doRemove(HttpServletRequest request)
	{
		request.getSession().removeAttribute(mKey);
	}

	/**
	 * Returns the private ScopeLocal lock object associated with this session.
	 * There is one lock Object which is shared across all ScopeLocals in a
	 * single session.
	 * 
	 * <p>Note that this method is synchronized on <code>this</code>. In other
	 * words, only one thread at a time can retrieve the lock, even if the
	 * threads are talking to different sessions. That's unfortunate, but this
	 * method is fast and shouldn't be a bottleneck. In a perfect world we'd
	 * just synchronize directly on the session and not have to create a
	 * separate lock object, but that approach has other issues and introduces
	 * the possibility of deadlock; we have no control over the servlet
	 * container possibly locking on the session as well.
	 *
	 * @param request current servlet request
	 * @return lock object to synchronize against
	 */
	protected synchronized Object getLock(HttpServletRequest request)
	{
		HttpSession session = request.getSession();
		Object lock = session.getAttribute(LOCK_KEY);
		if (lock == null)
		{
			lock = new Object();
			session.setAttribute(LOCK_KEY, lock);
		}
		return lock;
	}
}